/*
 * Web Crawler
 * Date: November 01, 2011
 * Group JEDi
 * Author(s): James Lovato, Efim Todorasco, Daniel Garrison
 */
package multithread;

import java.util.ArrayList;
import java.util.List;
import java.util.Observable;
import java.util.Observer;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicLong;

/**
 * This class stores all of the relevant information extracted from documents
 * by the Parser threads and sends requests to the ThreadSafeReporter to 
 * display this information on some kind of UI.  
 * 
 * @author James Lovato, Efim Todorasco, Daniel Garrison
 * @version Fall 2011
 */
public class ThreadSafeGatherer extends Observable {
	/**
	 * A thread safe reporter.
	 */
	private ThreadSafeReporter my_reporter;
	
	/**
	 * The total number of words from all parsed pages.
	 */
	private int my_total_words;
	
	/**
	 * Total number of all search words counted.
	 */
	private int my_total_words_counted;
	
	/**
	 * The total number of links from all parsed pages.
	 */
	private int my_total_links;
	
	/**
	 * The total number of pages parsed so far.
	 */
	// private int my_total_pages;
	
	/**
	 * The total number of each counted word.
	 */
	private int[] my_words_counted_totals;
	
	/**
	 * The number of pages to report before printing the final report.
	 */
	private int my_stop_page;
	
	/**
	 * The System.nanoTime() at the start of this crawl.
	 */
	private long my_start_time;
	
	/**
	 * A list of DataMetrics object collected from the Parser Threads.
	 */
	private List<DataMetrics> my_metrics_list;
	
	/**
	 * The number of pages reported so far.
	 */
	private AtomicInteger my_counter = new AtomicInteger();
	
	/**
	 * Is true when shutdown has been initiated.
	 */
	private boolean my_shutdown_flag = false;
	
	/**
	 * The total amount of time each thread spent parsing. 
	 */
	private AtomicLong my_total_parse_time = new AtomicLong();
	
	/**
	 * The total number of reports to be collected.
	 */
	private int my_number_of_reports_to_collect;
	
	/**
	 * Number of threads running.
	 */
	private int my_number_of_threads;
	
	private AtomicBoolean my_atomic_done = new AtomicBoolean(Boolean.FALSE);
	/**
	 * Thread safe data gatherer.
	 * @param the_page_count the number of pages to be parsed.
	 * @param the_search_words an array of words to search for during parsing.
	 */
	public ThreadSafeGatherer(int the_page_count, String[] the_search_words, Observer the_report_observer,
			int the_number_of_threads) {
		my_number_of_threads = the_number_of_threads;
		my_number_of_reports_to_collect = the_page_count;
		my_start_time = System.nanoTime();
		my_reporter = new ThreadSafeReporter(the_page_count, the_search_words);
		my_reporter.addObserver(the_report_observer);
		my_words_counted_totals = new int[the_search_words.length];
		my_metrics_list = new ArrayList<DataMetrics>(the_page_count);
	}

	/**
	 * Takes a DataMertics object, stores the object in a List, tallies the relevant data
	 * and passes the data on the reporter
	 * 
	 * @param the_metrics a data mertics object
	 */
	public synchronized void submit(DataMetrics the_metrics) {
		my_metrics_list.add(the_metrics);
		my_total_words += the_metrics.wordCount();
		my_total_links += the_metrics.linkCount();
		// my_total_pages++; // old counter.  Usin my_counter because it is an AtomicInteger
		my_total_parse_time.addAndGet(the_metrics.parseTime());
		int[] counts = the_metrics.hitCount();
		for (int i = 0; i < my_words_counted_totals.length; i++) {
			my_words_counted_totals[i] += counts[i];
			my_total_words_counted += counts[i];
		}
		my_reporter.reportPage(the_metrics, my_total_words, my_total_links, my_words_counted_totals, 
				my_total_parse_time.get());
		
		if (my_counter.incrementAndGet() == my_number_of_reports_to_collect) {
			// System.err.println("Calling finish()");
			finish();
		}
	}
	
	/**
	 * NOTE: THIS METHOD WILL BE DELETED AS IT IS NO LONGER NEEDED.
	 * Initiates the shutdown sequence.
	 * @param the_stop_page the last page to be printed
	 */
	public void initiateShutdownSequence(int the_stop_page) {
		my_shutdown_flag = true;
		my_stop_page = the_stop_page;
	}
	
	/**
	 * finish data collection and print the final report.
	 */
	private synchronized void finish() {
		if (my_atomic_done.get() == Boolean.FALSE) {
			my_reporter.finalReport(my_total_words_counted, my_total_links , System.nanoTime() - my_start_time, 
					my_number_of_threads, my_total_parse_time.get());
			setChanged();
			notifyObservers();
			my_atomic_done.set(Boolean.TRUE);
		}
	}
}
